﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DiscoveryLogic.UI.Layouts.DataAcccesses;
using DiscoveryLogic.UI.Layouts.DataAccesses;
using DiscoveryLogic.UI.Layouts.Models;
using DiscoveryLogic.UI.Layouts.ContentScreens;
using DiscoveryLogic.DataServices.DataSources;
using System.Globalization;

namespace DiscoveryLogic.UI.Layouts.Controls
{
    public partial class StockChart : UserControl
    {
        #region Private Variables
            
            // Mouse Managers
            // One for each slider
            private MouseManager mouseEventManager;
            private MouseManager mouseEventManager2;
            private MouseManager visibleRegionMouseManager;

            // Chart data for long term chart
            private DateTime[] dates;
            private double[] prices;
            // Chart data for upper chart
            private List<Trade> upperChartTradeList;
            // Comment: Ideally this would be the same set of data for both long term
            // and upper chart but because this chart is mocked up, it will do for now.
            //
            // The date range for the chart is calculated from the dates in the long term chart
            // so you should make sure that the earliest date in upperChartTradeList is NOT earlier
            // than the earliest date in 'dates' array.
            private TradeHistoryManager _tradeManager;
            // This flag indicates whether we have requested a news item selection change, so that we don't scroll to an item we've just told
            // the business object to highlight.
            private bool _firingNewsSelectionChangeEvent;

            private DateTime maxDate;
            private DateTime minDate;
            private double maxPrice;
            private double minPrice = 0;

            // Chart properties
            private double graphLeft;
            private double graphBottom;

            private double longTermGraphLeft;
            private double longTermGraphBottom;

            // We need the following variables because the actual
            // left property could be changing due to an animation
            private double VisibleRegionLeftPos;
            private double VisibleRegionOverlayLeftPos;
            private double LeftSliderThumbCanvasLeftPos;
            private double LeftSliderDividerLeftPos;
            private double RightSliderThumbCanvasLeftPos;
            private double RightSliderDividerLeftPos;

            private double ChartLinesLeftPos;
            private double ChartDetailsLeftPos;

            // Xaml
            private string graphLongNotch = "<Path xmlns='http://schemas.microsoft.com/client/2007' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' Width='1' Height='35.769' Stretch='Fill' Stroke='#FF666666' StrokeThickness='0.5' Canvas.ZIndex='1' Canvas.Left='123.416' Data='M135.475,149.5 L135.475,184.26908' />";
            private string graphShortNotch = "<Path xmlns='http://schemas.microsoft.com/client/2007' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' Width='0.5' Height='4.55' Fill='#FFFFFFFF' Stretch='Fill' Stroke='#FF666666' StrokeThickness='0.5' Canvas.ZIndex='1' Canvas.Left='57.668' Canvas.Top='28.75' Data='M58.475,175 L58.475,185.04988' />";
            
            public NewsMarkerEnter callTradeHistoryNewsItemEnter;
            public NewsMarkerLeave callTradeHistoryNewsItemLeave;
            public TradeMarkerEnter callTradeHistoryTradeMarkerEnter;
            public TradeMarkerLeave callTradeHistoryTradeMarkerLeave;

            private DashboardForm appContainer;

        #endregion

        #region Constructors

            public StockChart(DateTime[] dateArray, double[] priceArray, List<Trade> upperChartTradeList, DashboardForm container, ref TradeHistoryManager manager)
            {
                _tradeManager = manager;
                _firingNewsSelectionChangeEvent = false;
                
                InitializeComponent();


                //ValueBubbleValue.Text = ((double)this.Resources["Res_ValueBubble"]).ToCurrency();
                //x:Name="ValueBubbleValue" 

                appContainer = container;
                // Hook up to the trade history manager to get notified when the filtered news items change, or when properties of
                // the trade history change
                _tradeManager.PropertyChanged += TradeManager_PropertyChanged;
                _tradeManager.FilteredNews.CollectionChanged += FilteredNews_CollectionChanged;

                dates = dateArray;
                prices = priceArray;
                this.upperChartTradeList = upperChartTradeList;

                graphLeft = (double)Graph.GetValue(Canvas.LeftProperty);
                graphBottom = (double)Graph.GetValue(Canvas.TopProperty) + Graph.Height;

                longTermGraphLeft = 0;
                longTermGraphBottom = 0 + LongTermGraph.Height;

                // Initialize the following variables
                VisibleRegionLeftPos = (double)VisibleRegion.GetValue(Canvas.LeftProperty);
                VisibleRegionOverlayLeftPos = (double)VisibleRegionOverlay.GetValue(Canvas.LeftProperty);
                LeftSliderThumbCanvasLeftPos = (double)LeftSliderThumbCanvas.GetValue(Canvas.LeftProperty);
                LeftSliderDividerLeftPos = (double)LeftSliderDivider.GetValue(Canvas.LeftProperty);
                RightSliderThumbCanvasLeftPos = (double)RightSliderThumbCanvas.GetValue(Canvas.LeftProperty);
                RightSliderDividerLeftPos = (double)RightSliderDivider.GetValue(Canvas.LeftProperty);
                ChartLinesLeftPos = (double)ChartLines.GetValue(Canvas.LeftProperty);
                ChartDetailsLeftPos = (double)ChartDetails.GetValue(Canvas.LeftProperty);

                // Initialize mouse manager that controls the dragging
                mouseEventManager = new MouseManager("horizontal", 0, SliderBar.Width, RightSliderThumbCanvas.Width);
                mouseEventManager2 = new MouseManager("horizontal", 0, SliderBar.Width, LeftSliderThumbCanvas.Width);
                visibleRegionMouseManager = new MouseManager("horizontal", 0, SliderBar.Width, VisibleRegionOverlay.Width);

                // Attach Handlers
                AttachHandlers();

                // Based on the ranges, find max/min date and price
                FindMaxMin();

                // Plot all of the prices on the long term graph
                PlotLongTermGraph();

                // Position the visible blue region
                PositionVisibleRegion();

                // Draw upper main line chart according to longterm chart position
                DrawUpperChart();

                // Draw chart indicators/details
                DrawChartTradeMarkers();

                // Draw chart news markers
                DrawChartNewsMarkers();
            }

                     

        #endregion

        #region Get/Set Properties

            /// <summary>
            /// Set/Get prices array
            /// </summary>
            public double[] Prices
            {
                get { return prices; }
                set { prices = value; }
            }

            /// <summary>
            /// Set/Get dates array
            /// </summary>
            public DateTime[] Dates
            {
                get { return dates; }
                set { dates = value; }
            }


            /// <summary>
            /// Set the trades list to have its trade markers plotted
            /// on the upper line chart
            /// </summary>
            public List<Trade> UpperChartTradeList
            {
                get { return upperChartTradeList; }
                set { upperChartTradeList = value; }
            }

        #endregion

        #region Helper Methods

            /// <summary>
            /// Attach event handlers to events for this scenario
            /// </summary>
            private void AttachHandlers()
            {
                mouseEventManager.sliderMove += new MouseManager.SliderMoveEventHandler(OnRightSliderMove);
                mouseEventManager2.sliderMove += new MouseManager.SliderMoveEventHandler(OnLeftSliderMove);
                visibleRegionMouseManager.sliderMove += new MouseManager.SliderMoveEventHandler(OnVisibleRegionMove);

                RightSliderThumbCanvas.MouseEnter += mouseEventManager.OnMouseEnterHand;
                RightSliderThumbCanvas.MouseLeave += mouseEventManager.OnMouseLeaveArrow;
                RightSliderThumbCanvas.MouseLeftButtonDown += mouseEventManager.SliderMouseDown;
                RightSliderThumbCanvas.MouseLeftButtonUp += mouseEventManager.SliderMouseUp;
                RightSliderThumbCanvas.MouseMove += mouseEventManager.SliderMouseMove;

                LeftSliderThumbCanvas.MouseEnter += mouseEventManager2.OnMouseEnterHand;
                LeftSliderThumbCanvas.MouseLeave += mouseEventManager2.OnMouseLeaveArrow;
                LeftSliderThumbCanvas.MouseLeftButtonDown += mouseEventManager2.SliderMouseDown;
                LeftSliderThumbCanvas.MouseLeftButtonUp += mouseEventManager2.SliderMouseUp;
                LeftSliderThumbCanvas.MouseMove += mouseEventManager2.SliderMouseMove;

                VisibleRegionOverlay.MouseEnter += visibleRegionMouseManager.OnMouseEnterHand;
                VisibleRegionOverlay.MouseLeave += visibleRegionMouseManager.OnMouseLeaveArrow;
                VisibleRegionOverlay.MouseLeftButtonDown += visibleRegionMouseManager.SliderMouseDown;
                VisibleRegionOverlay.MouseLeftButtonUp += visibleRegionMouseManager.SliderMouseUp;
                VisibleRegionOverlay.MouseMove += visibleRegionMouseManager.SliderMouseMove;
            }

            /// <summary>
            /// Find and set the max/min date and price
            /// given that the dates and prices arrays are
            /// set
            /// </summary>
            private void FindMaxMin()
            {
                maxDate = dates[0];
                minDate = dates[0];
                maxPrice = 0;
                minPrice = 0;                

                // Loop through array of dates/prices
                // and find the max min range
                for (int i = 0; i < dates.Length; i++)
                {
                    // For dates
                    if (dates[i] > maxDate)
                    {
                        maxDate = dates[i];
                    }
                    else if (dates[i] < minDate)
                    {
                        minDate = dates[i];
                    }

                    // For prices
                    if (prices[i] > maxPrice)
                    {
                        maxPrice = prices[i];
                    }
                    else if (prices[i] < minPrice)
                    {
                        minPrice = prices[i];
                    }
                }
            }

            /// <summary>
            /// Plot long term graph
            /// </summary>
            private void PlotLongTermGraph()
            {
                // Create Path geometry and add path figure
                PathGeometry pg = new PathGeometry();
                pg.Figures = new PathFigureCollection();
                pg.Figures.Add(CreateLongTermGraphLine());

                // Create Path
                Path line = new Path();
                line.Stroke = new SolidColorBrush(Colors.White);
                line.StrokeThickness = 3;
                line.Fill = new SolidColorBrush(Colors.White);
                line.Data = pg;

                // Put path in Canvas
                line.SetValue(Canvas.ZIndexProperty, 1);
                LongTermGraph.Children.Add(line);

                // Draw notches on long term graph
                PositionLongTermGraphNotches();
            }

            /// <summary>
            /// Creates the lines of the long term graph
            /// </summary>
            /// <returns></returns>
            private PathFigure CreateLongTermGraphLine()
            {
                PathFigure pf = new PathFigure();
                pf.Segments = new PathSegmentCollection();
                
                // Add start and corner points
                pf.StartPoint = new Point(longTermGraphLeft, longTermGraphBottom - LongTermGraph.Height);

                LineSegment cornerSegment = new LineSegment();
                cornerSegment.Point = new Point(longTermGraphLeft + LongTermGraph.Width, longTermGraphBottom - LongTermGraph.Height);                
                pf.Segments.Add(cornerSegment);

                double pointX;
                double pointY;

                for (int i = 0; i < dates.Length; i++)
                {
                    // Calculate x coordinate
                    TimeSpan maxSpan = maxDate - minDate;
                    TimeSpan ts = dates[i] - minDate;
                    double percent = (double)ts.Days / (double)maxSpan.Days;
                    pointX = (LongTermGraph.Width * percent) + longTermGraphLeft;

                    // Calculate y coordinate
                    double maxDollarRange = maxPrice - minPrice;
                    double priceDifference = prices[i] - minPrice;
                    percent = priceDifference / maxDollarRange;
                    pointY = longTermGraphBottom - (LongTermGraph.Height * percent);

                    // Create line segment
                    LineSegment lineSegment = new LineSegment();
                    lineSegment.Point = new Point(pointX, pointY);

                    // Add line segment to path figure
                    pf.Segments.Add(lineSegment);
                }

                return pf;
            }

            /// <summary>
            /// Draw the time markers along the x-axis
            /// on the long term graph
            /// </summary>
            private void PositionLongTermGraphNotches()
            {
                // Find number of years/months/days between the start and end date
                TimeSpan ts = maxDate - minDate;
                
                int numYears = maxDate.Year - minDate.Year;
                int numMonths = maxDate.Month + (numYears * 12) - minDate.Month;


                for (int i = 0; i <= numYears; i++)
                {
                    int yearToPlot = minDate.AddYears(i).Year;
                    DateTime yearDate = new DateTime(yearToPlot, 1, 1);

                    // Draw a long notch for the year
                    double yearLeftPos = DrawNotch(yearDate, graphLongNotch, yearDate.Year.ToString());

                    // For each year, draw 3 small notches
                    for (int j = 1; j <= 11; j++)
                    {
                        DateTime monthDate = new DateTime(yearToPlot, yearDate.AddMonths(j).Month, 1);
                        
                        // If the notch date is greater than max date, then stop drawing notches
                        if (monthDate > maxDate)
                        {
                            break;
                        }

                        double monthLeftPos = DrawNotch(monthDate, graphShortNotch);
                    }
                }                
            }

            /// <summary>
            /// Place a marker on the chart to represent a date.
            /// </summary>
            /// <param name="date">Date that this marker represents</param>
            /// <param name="notchXaml">Xaml for the notch. Expects a path element.</param>
            /// <returns>Returns the calculated left position of the marker.
            ///          Returns -1 if the date is out of date range for the chart.</returns>
            private double DrawNotch(DateTime date, string notchXaml)
            {
                // Check if date is less than min date or greater than max date
                // If so, then don't draw
                if (date < minDate || date > maxDate)
                {
                    return -1;
                }

                // Create notch from xaml
                Path notch = XamlLoader<Path>.Create(notchXaml);

                // Calculate position
                TimeSpan ts = maxDate - minDate;
                double percent = (double)(date - minDate).Days / (double)ts.Days;

                notch.SetValue(Canvas.TopProperty, longTermGraphBottom - notch.Height);
                notch.SetValue(Canvas.LeftProperty, (percent * SliderBar.Width) + longTermGraphLeft + 0.5);

                // Place in canvas
                LongTermGraph.Children.Add(notch);

                // Return left position
                return (double)notch.GetValue(Canvas.LeftProperty);
            }

            /// <summary>
            /// Place a marker and label on the chart to represent a date.
            /// </summary>
            /// <param name="date">Date that this marker represents</param>
            /// <param name="notchXaml">Xaml for the notch. Expects a path element.</param>
            /// <param name="label">A string for the label to place beside this marker.</param>
            /// <returns>Returns the calculated left position of the marker.</returns>
            private double DrawNotch(DateTime date, string notchXaml, string label)
            {
                double left = DrawNotch(date, notchXaml);

                // If DrawNotch returns -1, then that means date was out
                // of range. Don't put label in this case.
                if (left < 0)
                {
                    return left;
                }

                TextBlock textLabel = CreateLongTermGraphLabel();
                textLabel.Text = label;
                
                // Position the label
                textLabel.SetValue(Canvas.LeftProperty, left + 2);
                textLabel.SetValue(Canvas.TopProperty, 2.0);

                // Add it to the canvas
                LongTermGraph.Children.Add(textLabel);

                // Return
                return left;
            }

            /// <summary>
            /// Creates and returns textblock in the format for the long term graph.
            /// </summary>
            /// <returns>A textblock</returns>
            private TextBlock CreateLongTermGraphLabel()
            {
                // Create textblock and set properties
                TextBlock label = new TextBlock();
                label.FontFamily = new FontFamily("Trebuchet MS");
                label.FontSize = 8;
                label.Foreground = new SolidColorBrush(Utils.HexToColor("#FF888888"));
                label.SetValue(Canvas.ZIndexProperty, 1);

                return label;
            }

            /// <summary>
            /// Position and set width of the blue visible region between the two sliders
            /// </summary>
            private void PositionVisibleRegion()
            {
                VisibleRegion.Width = RightSliderThumbCanvasLeftPos - LeftSliderThumbCanvasLeftPos;
                VisibleRegionOverlay.Width = VisibleRegion.Width;

                VisibleRegionLeftPos = LeftSliderThumbCanvasLeftPos + (LeftSliderThumbCanvas.Width / 2);
                VisibleRegion.SetValue(Canvas.LeftProperty, VisibleRegionLeftPos);

                VisibleRegionOverlayLeftPos = LeftSliderThumbCanvasLeftPos + (LeftSliderThumbCanvas.Width / 2);
                VisibleRegionOverlay.SetValue(Canvas.LeftProperty, VisibleRegionOverlayLeftPos);
            }

            /// <summary>
            /// Redraws the upper line chart according to what area is specified
            /// to be visible by the long term chart under the main chart
            /// </summary>
            private void DrawUpperChart()
            {
                // Positions of the left and right slider thumb of the long term chart
                double posLeftThumb = LeftSliderThumbCanvasLeftPos + LeftSliderThumbCanvas.Width / 2;
                double posRightThumb = RightSliderThumbCanvasLeftPos + RightSliderThumbCanvas.Width / 2;
                double graphWidth = SliderBar.Width;

                double visibleRegionWidth = posRightThumb - posLeftThumb;
                double visibleRegionPercent = visibleRegionWidth / graphWidth;

                // Set width of upper chart
                double newWidth = (1.0 / visibleRegionPercent) * graphWidth;
                ChartBlueArea.Width = newWidth;
                ChartBlueLine.Width = newWidth;

                // Also set width of company and index overlays
                CompanyOverlayArea.Width = newWidth;
                CompanyOverlayLine.Width = newWidth;

                SPIndexOverlayArea.Width = newWidth;
                SPIndexOverlayLine.Width = newWidth;

                NQIndexOverlayArea.Width = newWidth;
                NQIndexOverlayLine.Width = newWidth;

                DJIndexOverlayArea.Width = newWidth;
                DJIndexOverlayLine.Width = newWidth;

                WGIndexOverlayArea.Width = newWidth;
                WGIndexOverlayLine.Width = newWidth;
                
                // Get position of long term chart left slider thumb as a percent
                double positionAsPercent = (posLeftThumb - (double)SliderBar.GetValue(Canvas.LeftProperty)) / (SliderBar.Width);
                // Set position of upper chart
                double positionOfMainGraph = positionAsPercent * ChartBlueArea.Width;

                ChartLinesLeftPos = -positionOfMainGraph;
                ChartLines.SetValue(Canvas.LeftProperty, ChartLinesLeftPos);

                ChartDetailsLeftPos = -positionOfMainGraph;
                ChartDetails.SetValue(Canvas.LeftProperty, ChartDetailsLeftPos);
            }

            /// <summary>
            /// Draws chart trade markers.
            /// Only used initially to put the trade markers on the chart
            /// or used when we want to put a different set of trade markers
            /// on the chart
            /// </summary>
            private void DrawChartTradeMarkers()
            {
                ChartTradeMarkers.Children.Clear();

                // Plot trade indicators                
                for (int i = 0; i < upperChartTradeList.Count; i++)
                {
                    // Create and Add trade marker to canvas
                    CultureInfo culture = new CultureInfo("en-US");
                    TradeHistoryTradeMarker marker = new TradeHistoryTradeMarker(upperChartTradeList[i].IsBuy, upperChartTradeList[i].Id, Convert.ToDateTime(upperChartTradeList[i].Date, culture));
                    marker.tradeMarkerEntered += new MouseEventHandler(OnTradeMarkerEntered);
                    marker.tradeMarkerExited += new MouseEventHandler(OnTradeMarkerExited);
                    ChartTradeMarkers.Children.Add(marker);                    
                }

                // Position them
                RepositionTradeMarkers();
            }

            /// <summary>
            /// Used to reposition the trade markers.
            /// Used when user scales the graph
            /// </summary>
            private void RepositionTradeMarkers()
            {
                for (int i = 0; i < ChartTradeMarkers.Children.Count; i++)
                {
                    TradeHistoryTradeMarker marker = (TradeHistoryTradeMarker)ChartTradeMarkers.Children[i];

                    // Now position the marker                    
                    // Trade date should be greater than mindate
                    TimeSpan maxSpan = maxDate - minDate;
                    TimeSpan tspan = marker.TradeDate - minDate;
                    double percent = (double)tspan.Days / (double)maxSpan.Days;

                    double newPos = ChartBlueArea.Width * percent;
                    marker.SetValue(Canvas.LeftProperty, newPos);
                    marker.SetValue(Canvas.TopProperty, 100.0);
                }
            }

            /// <summary>
            /// Draws chart news markers
            /// </summary>
            private void DrawChartNewsMarkers()
            {
                ChartNewsMarkers.Children.Clear();

                // Plot trade indicators                
                foreach(TradeHistoryNewsItem item in _tradeManager.FilteredNews)
                {
                    // Create and Add trade marker to canvas
                    TradeHistoryNewsItemMarker newsMarker = new TradeHistoryNewsItemMarker();

                    newsMarker.NewsType = item.NewsType;
                    newsMarker.Symbol = item.Symbol;
                    newsMarker.NewsDate = item.NewsDate;
                    newsMarker.Id = item.Id;
                    newsMarker.ReflectionVisible = false;
                    newsMarker.Scale = 0.7;

                    newsMarker.newsItemEnter += new MouseEventHandler(newsMarker_newsItemEnter);

                    ChartNewsMarkers.Children.Add(newsMarker);
                }

                // Position them
                RepositionNewsMarkers();
            }

            /// <summary>
            /// Reposition the news markers
            /// </summary>
            private void RepositionNewsMarkers()
            {
                for (int i = 0; i < ChartNewsMarkers.Children.Count; i++)
                {
                    TradeHistoryNewsItemMarker newsMarker = (TradeHistoryNewsItemMarker) ChartNewsMarkers.Children[i];

                    // Now position the marker                    
                    // News date should be greater than mindate
                    TimeSpan maxSpan = maxDate - minDate;
                    TimeSpan tspan = newsMarker.NewsDate - minDate;
                    double percent = (double)tspan.Days / (double)maxSpan.Days;

                    double newPos = ChartBlueArea.Width * percent;
                    newsMarker.SetValue(Canvas.LeftProperty, newPos);
                    newsMarker.SetValue(Canvas.TopProperty, 20.0);
                }
            }

            /// <summary>
            /// Reposition the note markers
            /// </summary>
            private void RepositionNoteMarkers()
            {
                for (int i = 0; i < ChartNoteMarkers.Children.Count; i++)
                {
                    TradeHistoryNoteMarker noteMarker = (TradeHistoryNoteMarker)ChartNoteMarkers.Children[i];

                    // Now position the marker                    
                    // Note date that its placed on should be greater than mindate
                    TimeSpan maxSpan = maxDate - minDate;
                    TimeSpan tspan = noteMarker.DatePlaced - minDate;
                    double percent = (double)tspan.Days / (double)maxSpan.Days;

                    double newPos = ChartBlueArea.Width * percent;
                    noteMarker.SetValue(Canvas.LeftProperty, newPos);
                    
                }
            }

            /// <summary>
            /// Shift the long term chart by a percentage of its total width
            /// </summary>
            /// <param name="percent"></param>
            private void SetLongTermChart(double percent)
            {
                // Get left pos of the blue region
                double offset = percent * SliderBar.Width;
                
                // Set new positions
                VisibleRegionLeftPos += offset;
                VisibleRegionKeyFrame.Value = VisibleRegionLeftPos;

                VisibleRegionOverlayLeftPos += offset;
                VisibleRegionOverlayKeyFrame.Value = VisibleRegionOverlayLeftPos;

                LeftSliderThumbCanvasLeftPos += offset;
                LeftSliderThumbCanvasKeyFrame.Value = LeftSliderThumbCanvasLeftPos;

                LeftSliderDividerLeftPos += offset;
                LeftSliderDividerKeyFrame.Value = LeftSliderDividerLeftPos;

                RightSliderThumbCanvasLeftPos += offset;
                RightSliderThumbCanvasKeyFrame.Value = RightSliderThumbCanvasLeftPos;

                RightSliderDividerLeftPos += offset;
                RightSliderDividerKeyFrame.Value = RightSliderDividerLeftPos;

                ScrollLongTermChart.Begin();
            }

            /// <summary>
            /// When a user hovers over a trade marker on the chart
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void OnTradeMarkerEntered(object sender, MouseEventArgs e)
            {
                if (callTradeHistoryTradeMarkerEnter != null)
                {
                    callTradeHistoryTradeMarkerEnter(sender, e);
                }
                
            }

            /// <summary>
            /// When user's mouse leaves a trade marker on the chart
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            void OnTradeMarkerExited(object sender, MouseEventArgs e)
            {
                if (callTradeHistoryTradeMarkerLeave != null)
                {
                    callTradeHistoryTradeMarkerLeave(sender, e);
                }
            }

        #endregion

        #region Public Methods

            /// <summary>
            /// Show/hide the company overlay
            /// </summary>
            /// <param name="show"></param>
            public void ToggleCompanyOverlay(bool show)
            {
                if (show)
                {
                    CompanyOverlayLineToggleKeyFrame.Value = 0.8;
                    CompanyOverlayAreaToggleKeyFrame.Value = 0.8;
                }
                else
                {
                    CompanyOverlayLineToggleKeyFrame.Value = 0;
                    CompanyOverlayAreaToggleKeyFrame.Value = 0;                    
                }

                ToggleCompanyAnimation.Begin();
            }
            
            /// <summary>
            /// Clear the trade markers and Redraw them
            /// This is called from trade history.
            /// It is called after setting UpperChartTradeList property
            /// to change the trade markers shown.
            /// </summary>
            public void RedrawTradeMarkers()
            {
                DrawChartTradeMarkers();
            }

            /// <summary>
            /// Toggle visibility of the news markers on the line chart given
            /// a news type and a boolean indicating to show them or not
            /// </summary>
            /// <param name="newsType"></param>
            /// <param name="show"></param>
            public void ToggleNewsMarkers(NewsTypes newsType, bool show)
            {
                for (int i = 0; i < ChartNewsMarkers.Children.Count; i++)
                {
                    TradeHistoryNewsItemMarker marker = (TradeHistoryNewsItemMarker)ChartNewsMarkers.Children[i];
                    
                    if (marker.NewsType == newsType)
                    {
                        if (show)
                        {
                            marker.Visibility = Visibility.Visible;
                        }
                        else
                        {
                            marker.Visibility = Visibility.Collapsed;
                        }
                    }
                }
            }

            /// <summary>
            /// Given an id and news type, try to find the news marker on the
            /// line chart and then scroll to it
            /// </summary>
            /// <param name="id"></param>
            /// <param name="newsType"></param>
            public void FindNewsMarker(int id, NewsTypes newsType)
            {
                // Scroll through news markers canvas to find the right one
                TradeHistoryNewsItemMarker target = null;

                for (int i = 0; i < ChartNewsMarkers.Children.Count; i++)
                {
                    TradeHistoryNewsItemMarker marker = (TradeHistoryNewsItemMarker)ChartNewsMarkers.Children[i];

                    // Compare current marker's properties to the properties of the 
                    // one we're looking for
                    if (marker.Id == id && marker.NewsType == newsType)
                    {
                        target = marker;
                        break;
                    }
                }

                // If couldn't find the marker, then return
                if (target == null)
                {
                    return;
                }

                // Center of chart
                double chartCenterX = Math.Abs(ChartDetailsLeftPos) + UpperChartArea.Width/2;
                double offset = chartCenterX - (double)target.GetValue(Canvas.LeftProperty);
                double oldPos = ChartLinesLeftPos;
                double newPos = offset + ChartLinesLeftPos;

                // Make sure new position doesn't go past bounds
                if (newPos > 0)
                {
                    newPos = 0;
                }
                else if (ChartBlueArea.Width - Math.Abs(newPos) < UpperChartArea.Width)
                {
                    newPos = -(ChartBlueArea.Width - UpperChartArea.Width);
                }
                

                // Set chart     
                ChartLinesLeftPos = newPos;
                ChartDetailsLeftPos = newPos;

                ChartLinesKeyFrame.Value = newPos;
                ChartDetailsKeyFrame.Value = newPos;

                ScrollUpperChart.Begin();

                // Set long term chart
                double shiftPercent = (oldPos - newPos) / ChartBlueArea.Width;
                SetLongTermChart(shiftPercent);
            }

            /// <summary>
            /// Given id and isBuy properties from a Trade, find the corresponding
            /// marker on the line chart and scroll to it
            /// </summary>
            /// <param name="id"></param>
            /// <param name="isBuy"></param>
            public void FindTradeMarker(int id, bool isBuy)
            {
                // Scroll through news markers canvas to find the right one
                TradeHistoryTradeMarker target = null;

                for (int i = 0; i < ChartTradeMarkers.Children.Count; i++)
                {
                    TradeHistoryTradeMarker marker = (TradeHistoryTradeMarker)ChartTradeMarkers.Children[i];

                    // Compare current marker's properties to the properties of the 
                    // one we're looking for
                    if (marker.Id == id && marker.IsBuy == isBuy)
                    {
                        target = marker;
                        break;
                    }
                }

                // If couldn't find the marker, then return
                if (target == null)
                {
                    return;
                }

                // Center of chart
                double chartCenterX = Math.Abs(ChartDetailsLeftPos) + UpperChartArea.Width / 2;
                double offset = chartCenterX - (double)target.GetValue(Canvas.LeftProperty);
                double oldPos = ChartLinesLeftPos;
                double newPos = offset + ChartLinesLeftPos;

                // Make sure new position doesn't go past bounds
                if (newPos > 0)
                {
                    newPos = 0;
                }
                else if (ChartBlueArea.Width - Math.Abs(newPos) < UpperChartArea.Width)
                {
                    newPos = -(ChartBlueArea.Width - UpperChartArea.Width);
                }


                // Set chart               
                ChartLinesLeftPos = newPos;
                ChartDetailsLeftPos = newPos;

                ChartLinesKeyFrame.Value = newPos;
                ChartDetailsKeyFrame.Value = newPos;

                ScrollUpperChart.Begin();

                // Set long term chart
                double shiftPercent = (oldPos - newPos) / ChartBlueArea.Width;
                SetLongTermChart(shiftPercent);
            }

        #endregion

        #region Event Handlers

            /// <summary>
            /// Right slider of the long term chart moves
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnRightSliderMove(object sender, EventArgs e)
            {
                DiscoveryLogic.UI.Layouts.Models.CustomEventArgs args = (DiscoveryLogic.UI.Layouts.Models.CustomEventArgs)e;

                double newPosition = args.SliderThumbValue;

                // If new position is less than the left slider thumb position
                if (newPosition < LeftSliderThumbCanvasLeftPos + LeftSliderThumbCanvas.Width)
                {
                    newPosition = LeftSliderThumbCanvasLeftPos + LeftSliderThumbCanvas.Width;
                }
                
                // Set position of slider thumb
                RightSliderThumbCanvasLeftPos = newPosition;
                RightSliderThumbCanvas.SetValue(Canvas.LeftProperty, RightSliderThumbCanvasLeftPos);

                RightSliderDividerLeftPos = newPosition + (RightSliderThumbCanvas.Width / 2);
                RightSliderDivider.SetValue(Canvas.LeftProperty, RightSliderDividerLeftPos);

                // Position the visible blue region
                PositionVisibleRegion();

                // Redraw upper chart
                DrawUpperChart();

                // Reposition trade markers
                RepositionTradeMarkers();

                // Reposition news markers
                RepositionNewsMarkers();

                // Reposition note markers
                RepositionNoteMarkers();
            }

            /// <summary>
            /// Left Slider of the long term chart moves
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnLeftSliderMove(object sender, EventArgs e)
            {
                DiscoveryLogic.UI.Layouts.Models.CustomEventArgs args = (DiscoveryLogic.UI.Layouts.Models.CustomEventArgs)e;

                double newPosition = args.SliderThumbValue;

                // If new position is greater than the right slider thumb position
                if (newPosition > RightSliderThumbCanvasLeftPos - RightSliderThumbCanvas.Width)
                {
                    newPosition = RightSliderThumbCanvasLeftPos - RightSliderThumbCanvas.Width;
                }
                
                // Set position of slider thumb
                LeftSliderThumbCanvasLeftPos = newPosition;
                LeftSliderThumbCanvas.SetValue(Canvas.LeftProperty, LeftSliderThumbCanvasLeftPos);

                LeftSliderDividerLeftPos = newPosition + (LeftSliderThumbCanvas.Width / 2);
                LeftSliderDivider.SetValue(Canvas.LeftProperty, LeftSliderDividerLeftPos);

                // Position the visible blue region
                PositionVisibleRegion();

                // Redraw upper chart
                DrawUpperChart();

                // Reposition trade markers
                RepositionTradeMarkers();

                // Reposition news markers
                RepositionNewsMarkers();

                // Reposition note markers
                RepositionNoteMarkers();
            }

            /// <summary>
            /// User drags the region between the two sliders on the long term chart
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnVisibleRegionMove(object sender, EventArgs e)
            {
                DiscoveryLogic.UI.Layouts.Models.CustomEventArgs args = (DiscoveryLogic.UI.Layouts.Models.CustomEventArgs)e;

                double newPosition = args.SliderThumbValue;
                
                // Check that the new position doesn't exceed bounds
                if (newPosition < longTermGraphLeft)
                {
                    newPosition = longTermGraphLeft;
                }
                else if (newPosition + VisibleRegion.Width > longTermGraphLeft + LongTermGraph.Width)
                {
                    newPosition = (longTermGraphLeft + LongTermGraph.Width) - VisibleRegion.Width;
                }

                // Set position
                VisibleRegionLeftPos = newPosition;
                VisibleRegion.SetValue(Canvas.LeftProperty, VisibleRegionLeftPos);

                VisibleRegionOverlayLeftPos = newPosition;
                VisibleRegionOverlay.SetValue(Canvas.LeftProperty, VisibleRegionOverlayLeftPos);

                // Set slider thumb and slider thumb dividers positions as well
                LeftSliderThumbCanvasLeftPos = newPosition - (LeftSliderThumbCanvas.Width / 2);
                LeftSliderThumbCanvas.SetValue(Canvas.LeftProperty, LeftSliderThumbCanvasLeftPos);

                LeftSliderDividerLeftPos = newPosition;
                LeftSliderDivider.SetValue(Canvas.LeftProperty, LeftSliderDividerLeftPos);

                RightSliderThumbCanvasLeftPos = newPosition + VisibleRegion.Width - (RightSliderThumbCanvas.Width / 2);
                RightSliderThumbCanvas.SetValue(Canvas.LeftProperty, RightSliderThumbCanvasLeftPos);

                RightSliderDividerLeftPos = newPosition + VisibleRegion.Width;
                RightSliderDivider.SetValue(Canvas.LeftProperty, RightSliderDividerLeftPos);

                // Redraw upper chart
                DrawUpperChart();
            }

            /// <summary>
            /// Index overlay checkbox is clicked
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void IndexOverlayCheckBox_Click(object sender, EventArgs e)
            {
                bool check = (bool)((System.Windows.Controls.CheckBox)sender).IsChecked;

                double opacityValue;                

                if (check)
                {
                    opacityValue = 0.4;
                }
                else
                {
                    opacityValue = 0;
                }

                if (sender == NQCheckBox)
                {
                    NQOverlayAreaToggleKeyFrame.Value = opacityValue;
                    NQOverlayLineToggleKeyFrame.Value = opacityValue;

                    ToggleNQIndexAnimation.Begin();

                    if (check)
                    {
                        NQCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#99FF9900"));
                    }
                    else
                    {
                        NQCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#FF3B6161"));
                    }
                }
                else if (sender == WGCheckBox)
                {
                    WGOverlayAreaToggleKeyFrame.Value = opacityValue;
                    WGOverlayLineToggleKeyFrame.Value = opacityValue;

                    ToggleWGFundAnimation.Begin();

                    if (check)
                    {
                        WGCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#99003366"));
                    }
                    else
                    {
                        WGCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#FF3B6161"));
                    }
                }
                else if (sender == SPCheckBox)
                {
                    SPOverlayAreaToggleKeyFrame.Value = opacityValue;
                    SPOverlayLineToggleKeyFrame.Value = opacityValue;

                    ToggleSPIndexAnimation.Begin();

                    if (check)
                    {
                        SPCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#99FF0033"));
                    }
                    else
                    {
                        SPCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#FF3B6161"));
                    }
                }
                else if (sender == DJCheckBox)
                {
                    DJOverlayAreaToggleKeyFrame.Value = opacityValue;
                    DJOverlayLineToggleKeyFrame.Value = opacityValue;

                    ToggleDJIndexAnimation.Begin();

                    if (check)
                    {
                        DJCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#99FF0066"));
                    }
                    else
                    {
                        DJCheckBoxLabel.Foreground = new SolidColorBrush(Utils.HexToColor("#FF3B6161"));
                    }
                }

            }


            /// <summary>
            /// Add new note marker
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void UpperChart_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                // Get mouse positions
                double mouseX = e.GetPosition(ChartNoteMarkers).X;
                double mouseY = e.GetPosition(ChartNoteMarkers).Y;

                // Find date we're placing this note on
                double percent = (double)mouseX / (double)ChartBlueArea.Width;
                TimeSpan maxSpan = maxDate - minDate;                

                DateTime datePlaced = minDate.AddDays(percent * maxSpan.Days);

                // Close all note popups
                for (int i = 0; i < ChartNoteMarkers.Children.Count; i++)
                {
                    ((TradeHistoryNoteMarker)ChartNoteMarkers.Children[i]).ClosePopups();
                }

                // Create note marker

                double noteLeft = mouseX;
                double noteTop = mouseY;
                double chartCenterX = Math.Abs(ChartDetailsLeftPos) + UpperChartArea.Width / 2;
                double chartCenterY = UpperChartArea.Height / 2;
                //double XOffset = chartCenterX - noteLeft - 265 / 2;
                //double YOffset = chartCenterY - noteTop - 241 / 2;
                //double XOffset = (noteLeft - Math.Abs(ChartDetailsLeftPos)) * 0.9;
                //double YOffset = 50 + noteTop;
                double scaleFactor = (noteLeft - Math.Abs(ChartDetailsLeftPos)) / Graph.ActualWidth;
                scaleFactor = (scaleFactor < 0.1) ? 0.1 : scaleFactor; 

                double XOffset = ((noteLeft - Math.Abs(ChartDetailsLeftPos)) + (3 / scaleFactor))
                                  * appContainer.DashboardFormScale.ScaleX;
                double YOffset = (noteTop + 80) * appContainer.DashboardFormScale.ScaleY;

                TradeHistoryNoteMarker noteMarker = new TradeHistoryNoteMarker(DateTime.Today, datePlaced, XOffset, YOffset);
                noteMarker.noteEnter += new MouseEventHandler(OnNoteEnter);
                noteMarker.noteLeave += new MouseEventHandler(OnNoteLeave);

                // Place on canvas
                ChartNoteMarkers.Children.Add(noteMarker);
                // Position it
                noteLeft = noteLeft - noteMarker.ActualWidth;
                noteTop = noteTop - noteMarker.ActualHeight;
                noteMarker.SetValue(Canvas.LeftProperty, noteLeft);
                noteMarker.SetValue(Canvas.TopProperty, noteTop);
            }

            private void UpperChart_MouseMove(object sender, MouseEventArgs e)
            {
                // Get mouse positions
                double mouseX = e.GetPosition(Graph).X + 25;
                double mouseY = e.GetPosition(Graph).Y - 7;

                ValueBubble.SetValue(Canvas.LeftProperty, mouseX);
                ValueBubble.SetValue(Canvas.TopProperty, mouseY);
            }

            private void UpperChart_MouseEnter(object sender, MouseEventArgs e)
            {
                //ValueBubble.Visibility = Visibility.Visible;
            }

            private void UpperChart_MouseLeave(object sender, MouseEventArgs e)
            {
                //ValueBubble.Visibility = Visibility.Collapsed;
            }

            /// <summary>
            /// Raise the z order of the note being hovered over
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnNoteEnter(object sender, MouseEventArgs e)
            {
                TradeHistoryNoteMarker noteMarker = (TradeHistoryNoteMarker)sender;

                // Raise z value
                noteMarker.SetValue(Canvas.ZIndexProperty, 2);

                // Position Popup                
                double noteLeft = (double)noteMarker.GetValue(Canvas.LeftProperty) + noteMarker.Width;
                double noteTop = (double)noteMarker.GetValue(Canvas.TopProperty) + noteMarker.Height;
                double chartCenterX = Math.Abs(ChartDetailsLeftPos) + UpperChartArea.Width / 2;
                double chartCenterY = UpperChartArea.Height/2;
                //noteMarker.XOffset = chartCenterX - noteLeft - noteMarker.Width / 2;
                //noteMarker.YOffset = chartCenterY - noteTop - noteMarker.Height / 2;
                //noteMarker.XOffset = (noteLeft - Math.Abs(ChartDetailsLeftPos)) * 0.9;
                //noteMarker.YOffset = 50 + noteTop;
                double scaleFactor = (noteLeft - Math.Abs(ChartDetailsLeftPos)) / Graph.ActualWidth;
                scaleFactor = (scaleFactor < 0.1) ? 0.1 : scaleFactor;

                noteMarker.XOffset = ((noteLeft - Math.Abs(ChartDetailsLeftPos)) + (3 / scaleFactor))
                                     * appContainer.DashboardFormScale.ScaleX;
                noteMarker.YOffset = (noteTop + 80) * appContainer.DashboardFormScale.ScaleY;
                
                // Close all other note popups
                for (int i = 0; i < ChartNoteMarkers.Children.Count; i++)
                {
                    TradeHistoryNoteMarker otherMarker = (TradeHistoryNoteMarker)ChartNoteMarkers.Children[i];

                    if (otherMarker != noteMarker)
                    {
                        otherMarker.ClosePopups();
                        otherMarker.SetValue(Canvas.ZIndexProperty, 0);
                    }
                }
            }

            /// <summary>
            /// Reset the z order of the note being hovered over
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnNoteLeave(object sender, MouseEventArgs e)
            {
                ((TradeHistoryNoteMarker)sender).SetValue(Canvas.ZIndexProperty, 0);
            }

            /// <summary>
            /// This is the handler for the mouse leave event of news markers on 
            /// the chart
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void newsMarker_newsItemLeave(object sender, MouseEventArgs e)
            {
                // Clear the selected news item
                _tradeManager.SelectedNewsIndex = -1;
            }

            /// <summary>
            /// This is the handler for the event that is raise when the user hovers
            /// over a news marker on the chart
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void newsMarker_newsItemEnter(object sender, MouseEventArgs e)
            {
                int i = 0;

                foreach (UIElement child in ChartNewsMarkers.Children)
                {
                    TradeHistoryNewsItemMarker marker = child as TradeHistoryNewsItemMarker;

                    if (marker != null)
                    {
                        if (sender.Equals(marker))
                        {
                            // Ensure we make a note that we're setting the index, otherwise in the PropertyChanged event handler
                            // we'll scroll to the marker that we've just moused over!
                            _firingNewsSelectionChangeEvent = true;
                            _tradeManager.SelectedNewsIndex = i;
                        }
                    }
                    ++i;
                }
                _firingNewsSelectionChangeEvent = false;
            }

            /// <summary>
            /// The checkbox beside "My Portfolio" is clicked.
            /// This is used to show hide the portfolio
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void PortfolioCheckBox_Click(object sender, RoutedEventArgs e)
            {
                if ((bool)((System.Windows.Controls.CheckBox)sender).IsChecked)
                {
                    PortfolioLinesToggleKeyFrame.Value = 1;
                    ChartTradeMarkersToggleKeyFrame.Value = 1;
                    ChartNoteMarkersToggleKeyFrame.Value = 1;

                    TogglePortfolioAnimation.Begin();
                }
                else
                {
                    PortfolioLinesToggleKeyFrame.Value = 0;
                    ChartTradeMarkersToggleKeyFrame.Value = 0;
                    ChartNoteMarkersToggleKeyFrame.Value = 0;

                    TogglePortfolioAnimation.Begin();
                }
            }

            private void TradeManager_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "SelectedNewsIndex" && !_firingNewsSelectionChangeEvent && _tradeManager.SelectedNewsIndex > -1)
                {
                    TradeHistoryNewsItem item = _tradeManager.FilteredNews[_tradeManager.SelectedNewsIndex];

                    FindNewsMarker(item.Id, item.NewsType);
                }
            }

            private void FilteredNews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                DrawChartNewsMarkers();  
            }   

        #endregion

        #region Events and Delegates

            public delegate void NewsMarkerEnter(object sender, MouseEventArgs e);
            public delegate void NewsMarkerLeave(object sender, MouseEventArgs e);
            public delegate void TradeMarkerEnter(object sender, MouseEventArgs e);
            public delegate void TradeMarkerLeave(object sender, MouseEventArgs e);

        #endregion

            
    }
}
